
#ifndef IMAGE_TO_J2K_C
#define	IMAGE_TO_J2K_C
extern "C" {
#include "../inc/image_to_j2k.h"
}
#endif


#include "../inc/encoder.hpp"

Encoder::Encoder(char* workingDir, int width, int height) {
    this->workingDir = workingDir;
    this->width = width;
    this->height = height;
}

void Encoder::encodeTiffInJ2K(char* input_file, char *out_file) {
    encode_main(input_file, out_file, NULL);
}

void Encoder::encode(AVFrame* frame, char* outfile) {
    char tiffFilename[200];
    sprintf(tiffFilename, "%s/%s.tiff", this->workingDir, outfile);
    this->saveFrameToTiff(frame, tiffFilename);

    char outFileName[200];
    sprintf(outFileName, "%s/%s.j2c", this->workingDir, outfile);

    encode_main(tiffFilename, outFileName, NULL);
}

void Encoder::encodeFrameToJ2C(AVFrame* frame, char* outfile) {
    void* buffer = malloc(7000000 * sizeof (int));
    FILE* ppmFile = fmemopen(buffer, 7000000 * sizeof (int), "w");
    this->saveFrameToPPM(frame, ppmFile);
    fclose(ppmFile);
    char outFileName[200];
    sprintf(outFileName, "%s/%s.j2c", this->workingDir, outfile);
    ppmFile = fmemopen(buffer, 7000000 * sizeof (int), "r");
    encode_main("pouet.ppm", outFileName, ppmFile);
    fclose(ppmFile);
}

void Encoder::saveFrameToPPM(AVFrame *pFrame, FILE* pFile) {
    int y;

    // Write header
    fprintf(pFile, "P6\n%d %d\n255\n", width, height);

    // Write pixel data
    for (y = 0; y < height; y++)
        fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile);

}

int Encoder::saveFrameToTiff(AVFrame* frame, char* filename) {
    image_buffer_t* ret = this->frame_to_external(frame, width, height, "tiff");
    this->SaveExternal(ret, filename);
    free(ret->buffer);
    free(ret);
    return 0;
}

void Encoder::SaveExternal(image_buffer_t* image, char* filename) {
    FILE *pFile;

    pFile = fopen(filename, "wb");
    if (pFile == NULL)
        return;

    // Write pixel data
    fwrite(image->buffer, 1, image->size, pFile);
    // Close file
    fclose(pFile);
}

image_buffer_t* Encoder::frame_to_external(AVFrame* pFrame, int width, int height, char* format) {
    //frame_to_png(AVFrame *pFrame, int width, int height, char* format) {
    AVCodecContext *encoder_codec_ctx = NULL;
    AVCodec *encoder_codec = NULL;

    uint8_t *encoder_output_buffer = NULL;
    size_t encoder_output_buffer_size = NULL;
    int err;
    image_buffer_t* ret = (image_buffer_t*) malloc(sizeof (image_buffer_t));

    encoder_codec = avcodec_find_encoder_by_name(format);
    if (encoder_codec == NULL) {
        printf("Couldn't find a suitable encoder for : %s\n", format);
        ret->size = -1;
        return ret;
    }

    //TODO: We allocate 7 M, as TIFF 2048x1080 images are beetwen 6M and 6,5M as i saw
    encoder_output_buffer_size = 7000000 * sizeof (int);
    encoder_output_buffer = (uint8_t *) av_malloc(encoder_output_buffer_size);
    if (encoder_output_buffer == NULL) {
        printf("Failed to allocate the encoder output buffer\n");
        ret->size = -1;
        return ret;
    }

    encoder_codec_ctx = avcodec_alloc_context();
    if (encoder_codec_ctx == NULL) {
        printf("Failed to allocate the encoder codec context\n");
        av_free(encoder_output_buffer);
        ret->size = -1;
        return ret;
    }

    encoder_codec_ctx->width = width;
    encoder_codec_ctx->height = height;
    encoder_codec_ctx->pix_fmt = PIX_FMT_RGB24;

    if (avcodec_open(encoder_codec_ctx, encoder_codec) < 0) {
        printf("Failed to open the encoder\n");
        av_free(encoder_codec_ctx);
        av_free(encoder_output_buffer);
        ret->size = -1;
        return ret;
    }

    err = avcodec_encode_video(encoder_codec_ctx,
            encoder_output_buffer,
            encoder_output_buffer_size, pFrame);

    avcodec_close(encoder_codec_ctx);
    av_free(encoder_codec_ctx);

    ret->size = err;
    ret->buffer = encoder_output_buffer;

    return ret;
}

Encoder::~Encoder() {

}